﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.DataMining
{
    /// <summary>
    /// This class provides the import of numerical CSV files.
    /// </summary>
    [Serializable]
    public class CSVDataReader
    {
        /// <summary>
        /// The number of rows of the CSV file.
        /// </summary>
        private int rows;

        /// <summary>
        /// The number of columns of the CSV file.
        /// </summary>
        private int columns;

        /// <summary>
        /// The file, which includes the CSV data.
        /// </summary>
        private FileStream importFile;

        /// <summary>
        /// The element separator, which seperates the CSV data.
        /// </summary>
        private char elementSeparator;

        /// <summary>
        /// The number style of the CSV data.
        /// </summary>
        private NumberStyles numberStyle;

        /// <summary>
        /// The number format of the CSV data.
        /// </summary>
        private NumberFormatInfo numberFormatInfo;

        /// <summary>
        /// Initializes a new instance of the <see cref="CSVDataReader"/> class.
        /// </summary>
        /// <param name="importFile">The file, which includes the CSV data.</param>
        /// <param name="rows">The number of rows of the CSV file.</param>
        /// <param name="columns">The number of columns of the CSV file.</param>
        /// <param name="elementSeparator">The element separator, which seperates the CSV data.</param>
        /// <param name="nummberStyle">The number style of the CSV data.</param>
        /// <param name="numberFormatInfo">The number format of the CSV data.</param>
        public CSVDataReader(FileStream importFile, int rows, int columns, char elementSeparator,
                             NumberStyles nummberStyle, NumberFormatInfo numberFormatInfo)
        {
            if (rows < 0)
            {
                throw new ArgumentException("rows < 0", "rows");
            }

            if (columns < 0)
            {
                throw new ArgumentException("columns < 0", "columns");
            }

            this.rows = rows;
            this.columns = columns;
            this.importFile = importFile;
            this.numberStyle = nummberStyle;
            this.numberFormatInfo = numberFormatInfo;
            this.elementSeparator = elementSeparator;
        }

        /// <summary>
        /// Gets or sets the number of rows of the CSV file.
        /// </summary>
        /// <value>The number of rows of the CSV file.</value>
        public int Rows
        {
            get { return rows; }
            set { rows = value; }
        }

        /// <summary>
        /// Gets or sets te number of columns of the CSV file.
        /// </summary>
        /// <value>The number of columns of the CSV file.</value>
        public int Columns
        {
            get { return columns; }
            set { columns = value; }
        }

        /// <summary>
        /// Gets or sets the file, which includes the CSV data.
        /// </summary>
        /// <value>The file, which includes the CSV data.</value>
        public FileStream ImportFile
        {
            get { return importFile; }
            set { importFile = value; }
        }

        /// <summary>
        /// Gets or sets the element separator, which seperates the CSV data.
        /// </summary>
        /// <value>The element separator, which seperates the CSV data.</value>
        public char ElementSeparator
        {
            get { return elementSeparator; }
            set { elementSeparator = value; }
        }

        /// <summary>
        /// Gets or sets the number style of the CSV data.
        /// </summary>
        /// <value>The number style of the CSV data.</value>
        public NumberStyles NumberStyle
        {
            get { return numberStyle; }
            set { numberStyle = value; }
        }

        /// <summary>
        /// Gets or sets the number format of the CSV data.
        /// </summary>
        /// <value>The number format of the CSV data.</value>
        public NumberFormatInfo NumberFormatInfo
        {
            get { return numberFormatInfo; }
            set { numberFormatInfo = value; }
        }

        /// <summary>
        /// Imports the CSV data out of the specified file.
        /// </summary>
        /// <returns>The data of the CSV file.</returns>
        public double[,] ImportData()
        {
            double[,] result = new double[this.rows,this.columns];
            StreamReader reader = new StreamReader(this.importFile);

            for (int i = 0; i < this.rows; i++)
            {
                if (reader.EndOfStream)
                {
                    throw new ArgumentException("The number of expected rows cannot be found.");
                }

                String[] tempuri = reader.ReadLine().Split(new char[] {this.elementSeparator},
                                                           StringSplitOptions.RemoveEmptyEntries);

                if (tempuri.Length < this.columns)
                {
                    throw new ArgumentException("The number of expected columns cannot be found.");
                }

                for (int j = 0; j < this.columns; j++)
                {
                    result[i, j] = Double.Parse(tempuri[j].Trim(), this.numberStyle, this.numberFormatInfo);
                }
            }

            return result;
        }
    }
}